在內層的元件中開一個缺口給外層的元件使用,所以外部的元件可以直接操作內層元件的HTML結構。
我們可以透過 slot 從外層直接操作內層的HTML結構。
在需要設置插巢的地方加上 <slot>
標籤。
<card>
<p>這是由外層定義的</p>
</card>
app.component('card', {
template: `<div class="card" style="width: 18rem;">
<div class="card-header">
元件 Header
</div>
<div class="card-body">
//在這裡加入 <slot> 標籤
<slot></slot>
</div>
<div class="card-footer">
元件 Footer
</div>
</div>`
})
在開發過程中,如果我們開放插巢的話,建議提供預設值。 當我們外層有內容時就使用外層的,外層沒內容就使用預設值。
而預設值直接在 slot
內插入內容即可。
<card></card>
app.component('card', {
template: `<div class="card" style="width: 18rem;">
<div class="card-header">
元件 Header
</div>
<div class="card-body">
<slot><p>這是預設值</p></slot>
</div>
<div class="card-footer">
元件 Footer
</div>
</div>`
})
如果我們希望能夠開放多個插巢呢?
我們可以直接開三個 slot
。 但要特別注意,當我們開三個 slot
,但沒有 特別給予名稱 的話,外層就不知道哪個結構是對應哪個插巢。
因此我們可以在 slot
上加入 name=""
。
app.component('card2', {
template: `<div class="card" style="width: 18rem;">
<div class="card-header">
<slot name="header">元件 Header</slot>
</div>
<div class="card-body">
<slot>這段是預設的文字</slot>
</div>
<div class="card-footer">
<slot name="footer">元件 Footer</slot>
</div>
</div>`
});
並在HTML結構上使用 template
標籤並加上 v-slot=" "
,預設內容則使用 v-slot:default
。
<card2>
<template v-slot:header>我喜歡這張卡片</template>
<!-- 預設請加入 default -->
<template v-slot:default>這是卡片 2 號</template>
<template v-slot:footer>這是卡片腳</template>
</card2>
具名插巢也有縮寫,v-slot
可以直接縮寫成 #
,會得到一樣的結果。
<card2>
<template #header>我喜歡這張卡片</template>
<!-- 預設請加入 default -->
<template #default>這是卡片 2 號</template>
<template #footer>這是卡片腳</template>
</card2>
在元件的概念裡,外層和內層元件的資料狀態是分離的,兩個資料狀態各自獨立,因此在插巢區塊,一樣不能取得內元件的資料狀態。
我們可以透過將元件插巢中的部分資料狀態,透過 props
傳送給外層做使用,這也稱為 slot props
。
v-slot:default
對應插巢位置v-slot:default
之後賦予值的名稱<card>
<!-- 取值的名稱 -->
<template v-slot:default ="slotProps">
我想取出元件的值來使用
{{slotProps}}
<br><br>
{{slotProps.product.name}}
</template>
</card>
app.component('card', {
data() {
return {
product: {
name: '蛋餅',
price: 30,
vegan: false
},
}
},
template: `<div class="card" style="width: 18rem;">
<div class="card-body" >
<slot :product="product"></slot>
</div>
</div>`
});
:product="product"
:veganName="veganName
傳出來v-slot:default
之後賦予一個物件 : {product,veganName}
<card2 :product="product">
<template #header>
買早餐
</template>
<template #default ="{product,veganName}">
{{ product }}
{{ veganName }}
</template>
</card2>
app.component('card2', {
props: ['product'],
data() {
return {
veganName: ''
}
},
created() {
this.veganName = this.product.vegan ? '素食' : '非素食';
},
template: `<div class="card" style="width: 18rem;">
<div class="card-body" >
<slot :product="product" :veganName="veganName"></slot>
</div>
</div>`
})
假如我們少傳出:veganName="veganName"
,我們也可以設定預設值。
<card2 :product="product">
<template #header>
買早餐
</template>
<template #default ="{product,veganName = '是素非素'}">
{{ product }}
{{ veganName }}
</template>
</card2>
app.component('card2', {
props: ['product'],
data() {
return {
veganName: ''
}
},
created() {
this.veganName = this.product.vegan ? '素食' : '非素食';
},
template: `<div class="card" style="width: 18rem;">
<div class="card-body" >
<slot :product="product" ></slot>
</div>
</div>`
})
JS
<script type="module">
const app = Vue.createApp({
data() {
return {
product: {
name: '蛋餅',
price: 30,
vegan: false
}
}
},
});
app.component('card', {
data() {
return {
product: {
name: '蛋餅',
price: 30,
vegan: false
},
}
},
template: `<div class="card" style="width: 18rem;">
<div class="card-body" >
<slot :product="product"></slot>
</div>
</div>`
});
app.component('card2', {
props: ['product'],
data() {
return {
veganName: ''
}
},
created() {
this.veganName = this.product.vegan ? '素食' : '非素食';
},
template: `<div class="card" style="width: 18rem;">
<div class="card-body" >
<slot :product="product" :veganName="veganName"></slot>
</div>
</div>`
})
app.mount('#app');
</script>